package de.fhtrier.gdw2.ships.game;

import java.util.ArrayList;

import org.lwjgl.input.Mouse;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.state.StateBasedGame;

import de.fhtrier.gdw.commons.netcode.datagram.INetDatagram;
import de.fhtrier.gdw2.ships.game.entities.Entity;
import de.fhtrier.gdw2.ships.game.interfaces.ISelecteable;
import de.fhtrier.gdw2.ships.game.modules.Module;
import de.fhtrier.gdw2.ships.network.datagrams.LevelDatagram;
import de.fhtrier.gdw2.ships.utils.AssetLoader;

public class World
	{

		// * Entities existing in the World */
		private ArrayList<Entity> entities;
		private Contextmenue menue;

		private Mouse m;
		/**
		 * Currently selected Entities - Shown in the Contextmenue - following
		 * commands
		 */
		private ArrayList<ISelecteable> selected;
		/** Soll ein Selection-Rect gezeichnet werden? */
		private boolean selectionRect;
		private float rectX, rectY, rectWid, rectHeight;
		private float offsetX = 0;
		private float offsetY = 0;
		private boolean midButtonPressed = false;

		public World() throws SlickException
			{
				entities = new ArrayList<Entity>();

				AssetLoader.getInstance();

				selected = new ArrayList<ISelecteable>();
				System.out.println(selected.toString());

				menue = new Contextmenue(this);
			}

		public void render(GameContainer container, StateBasedGame game,
				Graphics g) throws SlickException
			{

				g.pushTransform();
				g.translate(getOffsetX(), getOffsetY());

				// Entities Rendern
				for (Entity e : entities)
					{
						e.render(container, game, g);
					}

				// Auswahlrechteck...
				if (selectionRect)
					{
						g.drawRect(rectX, rectY, rectWid, rectHeight);
					}

				g.popTransform();

				g.fillRect(0, 0, 10, 740,
						AssetLoader.getInstance().getImage("mod_central"), 0, 0);
				g.fillRect(1540, 0, 10, 1500, AssetLoader.getInstance()
						.getImage("mod_central"), 0, 0);
				g.fillRect(0, 0, 1540, 20,
						AssetLoader.getInstance().getImage("mod_central"), 0, 0);
				g.fillRect(0, 740, 1600, 20, AssetLoader.getInstance()
						.getImage("mod_central"), 0, 0);

				// Ein hübsches Rechteck zeichnen...
				g.drawRect(10, 10, 1530, 730);

				// Menü Rendern
				menue.render(container, game, g, this);

			}

		public void update(GameContainer container, StateBasedGame game,
				int delta) throws SlickException
			{

				// update entities
				for (Entity e : entities)
					{
						e.update(this, container, delta);
					}

				if (!selected.isEmpty())
					{
						this.keyMovement(container);
					}

				// menue.update(this);
				// update other stuff...

				/*
				 * if(levelDelta > this.levelSpeed) { this.currentBlock.move(0,
				 * 1); if(collision()) { //Block bewegen currentBlock.move(0,
				 * -1); //BLock im Feld verankern und dann l�schen, neuer BLock
				 * etc...
				 * 
				 * for(int y = 0; y < currentBlock.getSize();y++) { for(int x =
				 * 0; x < currentBlock.getSize();x++) {
				 * if(currentBlock.getData()[x][y] != 's') {
				 * this.field[currentBlock.getX() + x][currentBlock.getY() + y]
				 * = currentBlock.getData()[x][y]; } }
				 * 
				 * }
				 * 
				 * int deletedRows = this.checkRows();
				 * 
				 * 
				 * score += level * Constants.ROWSCORE * deletedRows *
				 * deletedRows;
				 * 
				 * while(rowCount / Constants.ROWSPERLEVEL >= level) { level++;
				 * this.levelSpeed *= Constants.SPEEDLVLFAKTOR; }
				 * 
				 * currentBlock = nextBlock;
				 * currentBlock.move(Constants.LEVELWIDTH/2 -2, 0); nextBlock =
				 * RandomBlockGen.randomBrick();
				 * 
				 * // Wenn Steine am oberen Rand if(collision()) {
				 * this.gameOver=true; }
				 * 
				 * } levelDelta -= levelSpeed; }
				 */
			}

		public void keyPressed(int key, char c)
			{
				int speeed = 5;
				switch (key)
					{
					case Input.KEY_SPACE:
						// shießen
						break;
					case Input.KEY_C:
						this.offsetX = 0;
						this.offsetY = 0;
						break;
					default:
						break;
					}

			}

		public void keyReleased(int key, char c)
			{
				// TODO empty
			}

		private void keyMovement(GameContainer container)
			{
				boolean keyPressed = false;
				/**
				 * @ param press kann 9 Zustände annehmen. Entsprechend den
				 * gedrückten Tasten. Der Wert 4 hat dabei die Bedeutung dass
				 * alle gedrückten Tasten sich aufheben. Er stellt sicher, dass
				 * press nicht negativ wird.
				 * */
				byte press = 4;
				double speed = 5;

				if (container.getInput().isKeyDown(Input.KEY_UP))
					{
						press -= 1;
						keyPressed = true;
					}

				if (container.getInput().isKeyDown(Input.KEY_DOWN))
					{
						press += 1;
						keyPressed = true;
					}

				if (container.getInput().isKeyDown(Input.KEY_LEFT))
					{
						press -= 3;
						keyPressed = true;
					}

				if (container.getInput().isKeyDown(Input.KEY_RIGHT))
					{
						press += 3;
						keyPressed = true;
					}

				if (keyPressed)
					{
						int dir;
						// Hier werden aus dem press-Muster die Werte
						// ausgelesen:
						// Rotation
						switch (press)
							{
							case 0:
								dir = 315;
								break;

							case 1:
								dir = 270;
								break;

							case 2:
								dir = 225;
								break;

							case 3:
								dir = 0;
								break;
							case 4:
							default:
								dir = selected.get(0).getDirection();
								break;

							case 5:
								dir = 180;
								break;

							case 6:
								dir = 45;
								break;

							case 7:
								dir = 90;
								break;

							case 8:
								dir = 135;
								break;
							}
						
						// Erstes ternär-bit: x-Bewegung
						int mx = (press / 3) - 1; 
						// Zweites Ternär-bit:	y-Bewegung
						int my = (press % 3) - 1; 

						speed = press%2 == 0 ? speed/1.414 : speed;
						
						selected.get(0).move(mx * speed, my * speed);
						selected.get(0).setDirection(dir);
					}
			}

		private boolean collision()
			{
				return false;
				// TODO
			}

		/*
		 * public void reveiveDatagram(LevelDatagram datagram) { field =
		 * datagram.getField(); currentBlock = new
		 * BlockDummy(datagram.getCurrentBlockData()); nextBlock = new
		 * BlockDummy(datagram.getNextBlockData());
		 * currentBlock.move(datagram.getCurrentBlockX(),
		 * datagram.getCurrentBlockY()); this.score = datagram.getScore();
		 * this.level = datagram.getLevel(); this.rowCount = datagram.getRows();
		 * }
		 * 
		 * public LevelDatagram getDatagram() { return new LevelDatagram(field,
		 * currentBlock, nextBlock, this); }
		 */

		public INetDatagram getDatagram()
			{
				// TODO Auto-generated method stub
				return null;
			}

		public void reveiveDatagram(LevelDatagram datagram)
			{
				// TODO Auto-generated method stub

			}

		// ---------------------------------
		// ---------------------------------
		// Manipulators
		// ---------------------------------
		// ---------------------------------

		public void addEntity(Entity e)
			{
				entities.add(e);
			}

		public void removeEntity(Entity e)
			{
				entities.remove(e); // Entity aus Liste entfernen
			}

		public Entity getEntity(int i)
			{
				return entities.get(i);
			}

		// ---------------------------------
		// Getter und Setter
		// ---------------------------------

		public ArrayList<Entity> getEntities()
			{
				// TODO Auto-generated method stub
				return entities;
			}

		// ---------------------------------
		// ---------------------------------
		// Static Methods
		// ---------------------------------
		// ---------------------------------

		public static boolean inRange(Module m, Entity e, int range)
			{
				double x = e.getX() + m.getX(); // TODO: X-Verschiebung von Modul
												// zu Ziel
				double y = e.getY() + m.getY(); // TODO: Y-Verschiebung von Modul
												// zu Ziel

				if (range * range <= x * x + y * y) // Entfernung in Reichweite?
					{
						return true;
					}
				return false;
			}

		public void mouseMoved(int oldx, int oldy, int newx, int newy)
			{
				// TODO

			}

		public void mousePressed(int button, int x, int y)
			{
				switch (button)
					{
					case 0:
						{
							this.rectX = x - getOffsetX();
							this.rectY = y - getOffsetY();
							this.rectHeight = 0;
							this.rectWid = 0;
							this.selectionRect = true;
						}
						break;
					case 1:
						break;
					case 2:
						{
							this.midButtonPressed = true;
						}
						break;
					}

			}

		public void mouseReleased(int button, int x, int y)
			{
				switch (button)
					{
					case 0:
						{
							this.selectionRect = false;
							selectGroup(rectX, rectY, rectX + rectWid, rectY
									+ rectHeight);
						}
						break;

					case 1:
						break;

					case 2:
						{
							this.midButtonPressed = false;
						}
						break;

					}

			}

		public void mouseDragged(int oldx, int oldy, int newx, int newy)
			{

				rectHeight += (newy - oldy);
				rectWid += (newx - oldx);
				if (this.midButtonPressed)
					{
						this.setOffsetX(this.getOffsetX() + (newx - oldx));
						this.setOffsetY(this.getOffsetY() + (newy - oldy));
					}
			}

		public void mouseClicked(int button, int x, int y, int clickCount)
			{
				switch (button)
					{
					case 0:
						selectSingleEntity(x, y);
						break;
					case 1:
						// give selected entities order according the clicked
						// position
						rightClickOrder(x - this.getOffsetX(), y - getOffsetY());
						break;
					case 2:
						break;
					}
			}

		private void selectSingleEntity(int x, int y)
			{
				System.out.println("LeftClickOrder");
				// find ISelecteable next to X/Y
				ISelecteable sel = getEntityAtPosition(x, y);
				// Nur noch sel selectiert
				if (sel != null)
					{
						System.out.println(sel.getTypeName());
						this.selected.clear();
						this.selected.add(sel);
					} else
					{
						System.out.println(" - - - null - - - ");
					}

			}

		private void selectGroup(float x1, float y1, float x2, float y2)
			{
				float bigx = x1 > x2 ? x1 : x2;
				float smallx = x1 > x2 ? x2 : x1;
				float bigy = y1 > y2 ? y1 : y2;
				float smally = y1 > y2 ? y2 : y1;
				boolean first = true;

				for (Entity e : this.getEntities())
					{
						if (e instanceof ISelecteable)
							{
								if (e.getX() + e.getRadius() > smallx
										&& e.getX() - e.getRadius() < bigx
										&& e.getY() + e.getRadius() > smally
										&& e.getY() - e.getRadius() < bigy)
									{
										if (first)
											{
												selected.clear();
												first = false;
											}
										this.selected.add((ISelecteable) e);
									}
							}
					}

			}

		public ISelecteable getEntityAtPosition(float x, float y)
			{

				Entity lastFound = null;
				// Pythagoras' c-quadrat wobei c der radius des Schildes ist..
				// 100 ist eine geschätzte Zahl, nicht korrekt
				// später variabel..
				double lastDistance2 = 70 * 70;
				double actualDistance2;

				for (Entity e : this.getEntities())
					{
						if (e instanceof ISelecteable)
							{
								actualDistance2 = (e.getX() - x)
										* (e.getX() - x) + (e.getY() - y)
										* (e.getY() - y);
								if (lastDistance2 > actualDistance2)
									{
										lastDistance2 = actualDistance2;
										lastFound = e;
									}
							}
					}

				return (ISelecteable) lastFound;
			}

		private void rightClickOrder(float f, float g)
			{

				for (ISelecteable e : this.getSelected())
					{
						e.rightClickOrder(this, f, g);
					}

			}

		public ArrayList<ISelecteable> getSelected()
			{
				return selected;
			}

		public void selectEntity(ISelecteable e)
			{
				this.selected.add(e);
			}

		public static Vector2f vectorFromDir(float dir)
			{
				float angle = (dir + 270) % 360;

				angle -= 360;
				angle *= (-1);

				Vector2f v = new Vector2f(angle);

				return v;
			}

		public float getOffsetX()
			{
				return offsetX;
			}

		public void setOffsetX(float offsetX)
			{
				this.offsetX = offsetX;
			}

		public float getOffsetY()
			{
				return offsetY;
			}

		public void setOffsetY(float offsetY)
			{
				this.offsetY = offsetY;
			}

	}
